<?php
/**
 *  Abstract reference property for referenced single objects
 *  Examples of Reference: ForeignKey, OneToOne
 *
 */
abstract class Yz_Property_Reference
    extends Yz_Property_Abstract
    implements Yz_Property_Interface_Reference
{

    protected $model;

    /** Temporarily store item ID: ForeignKey mappings for self-referenced relationships
     *  @var array
     */
    protected $id2key = array();

    /**
     *  @param Yz_Model_Interface|null $model => null for 'compatible' __construct
     */
    public function __construct( Yz_Model_Interface $model, $config=null )
    {
        debug();
        $this->init(array_slice(func_get_args(),1));
        $this->model = $model;
    }

    /**
     *
     */
    public function getModel()
    {
        return $this->model;
    }


    /** Test if a value is suitable for the property, possibly adjust
     *  @param mixed value
     *  @return the adjusted value
     *  @throw InvalidArgument
     */
    public function testValue($value)
    {
        $value = parent::testValue($value);
        if (null === $value)
            return null;
        if (! ($value instanceof Yz_Item_Interface)
                or ! $value->isItemOf($this->model))
            throw new Yz_Property_Exception("Value must be a valid object of {$this->model}");
        return $value;
    }



    /** Select a default value for an item, based on the property name
     *  @param string $propName
     *  @param Yz_Item_Interface $item
     *  @return default value
     *  @throw Yz_Property_Exception
     */
    public function selectValue($propName, Yz_Item_Interface $item)
    {
        $id = $item->id;
        debug("Prop {$propName}: Item id=$id");
        if (! $id or ! isset($this->id2key[$id]))
            return null;
        $fkey = $this->id2key[$id];
        return $this->model->fetchItemById($fkey);
    }


    /** Assign a value for an item from an array of values, with prefix
     *  @param string $propName
     *  @param array-type $values
     *  @param string $prefix
     *  @return value
     *  @throw Yz_Property_Exception
     */
    public function assignValue(Yz_Model_Interface $model, $propName, $values, $prefix)
    {
        // if a reference to self
        if ($model === $this->model) {
            $prefix or $prefix = $model->getName() . '_';
            $itemId = $values[$prefix . 'id'];
            $refId = $values[$prefix . $propName . 'Id']
                and $this->id2key[(int)$itemId] = (int)$refId;
            return $this; // a temporary value which will force Item to re-evaluate
        }
        $refPrefix = $prefix . ucfirst($propName) . '_';
        $refId = $values[$refPrefix . 'id'];
        if (! $refId)
            return null;

        return $this->model->itemFromValues($values, $refPrefix);
    }

}
